using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using DS.SludgeEngine.Base;

namespace DS.SludgeEngine.Input
{
    public class MouseController : DrawableGameComponent, IMouseController
    {
        public MouseController(Game game)
            : base(game)
        {

        }

        private MouseStates state = MouseStates.Rest;
        private MouseState currentState;
        private MouseState previousState;

      

        private Vector2 start = new Vector2();
        private Vector2 end = new Vector2();
        private bool dragged = false;

        public override void Draw(GameTime gameTime)
        {
            previousState = currentState;
            currentState = Mouse.GetState();

            ICameraManager camera = (ICameraManager)base.Game.Services.GetService(typeof(ICameraManager));
            IDrawing2DManager drawing = (IDrawing2DManager)base.Game.Services.GetService(typeof(IDrawing2DManager));
            IEntityController entities = (IEntityController)base.Game.Services.GetService(typeof(IEntityController));

            if (state == MouseStates.Dragging)
            {
                drawing.AddRectangle(start, end, Color.Red, false);
            }
        }
        


        public float MouseWheelStatus
        {
            get
            {
                int currentWheelValue = currentState.ScrollWheelValue;
                int previousWheelValue = previousState.ScrollWheelValue;

                if (currentWheelValue > previousWheelValue)
                {
                    return -1.0f;
                }
                else if (currentWheelValue < previousWheelValue)
                {
                    return 1.0f;
                }
                else
                {
                    return 0.0f;
                }
            }
        }
        public MouseState CurrentState
        {
            get { return currentState; }
        }

        public MouseState PreviousState
        {
            get { return previousState; }
        }


        public override void Update(GameTime gameTime)
        {
            IEntityController entities = (IEntityController)base.Game.Services.GetService(typeof(IEntityController));
            ICameraComponent camera = (ICameraComponent)base.Game.Services.GetService(typeof(ICameraComponent));
            ITerrainComponent terrain = (ITerrainComponent)base.Game.Services.GetService(typeof(ITerrainComponent));
            MouseState mouseState = Mouse.GetState();

            switch (state)
            {
                case MouseStates.Rest:
                    Rest(mouseState, entities, camera, terrain);
                    break;
                case MouseStates.Click:
                    Click(mouseState, camera);
                    break;
                case MouseStates.Dragging:
                    Dragging(mouseState, entities, camera);
                    break;
                case MouseStates.Release:
                    Release(mouseState, entities);
                    break;
                default:
                    break;
            }


            if (mouseState.RightButton == ButtonState.Pressed)
            {
                mouseState = PerformAction(entities, camera, terrain, mouseState);
            }
        }

        private MouseState PerformAction(IEntityController entities, ICameraComponent camera, ITerrainComponent terrain, MouseState mouseState)
        {
            if (entities != null)
            {
                if (entities.HighlightedActor != null)
                {
                    entities.SetAttack();
                }
                else if (entities.SelectedActors.Count > 0)
                {
                    Vector2 position = new Vector2();

                    position.X = mouseState.X;
                    position.Y = mouseState.Y;

                    Ray mouseRay = GetRay(position, camera);
                    Vector3 goTo = terrain.PickTerrain(mouseRay, camera.Position);

                    entities.SetDestination(goTo);
                }
            }
            return mouseState;
        }

        private void Release(MouseState mouseState, IEntityController entities)
        {
            if (dragged)
            {
                if (entities != null)
                {
                    entities.Dragged();
                }
                dragged = false;
            }
            else
            {
                if (entities != null)
                {
                    entities.SetSelected();
                }
            }
            state = MouseStates.Rest;
        }

        private void Click(MouseState mouseState, ICameraComponent camera)
        {
            if (GameInformation.CreateUnit)
            {
                newUnit = null;
                GameInformation.CreateUnit = false;
            }
            else
            {
                if (Compare(mouseState))
                {
                    state = MouseStates.Dragging;
                    dragged = true;
                }
                if (mouseState.LeftButton == ButtonState.Released)
                {
                    state = MouseStates.Release;
                }
            }
        }

        private void Dragging(MouseState mouseState, IEntityController entities, ICameraComponent camera)
        {
            Vector3 end3D = GetCoordinates(new Vector2(mouseState.X, mouseState.Y), camera);
            end = new Vector2(mouseState.X, mouseState.Y);


            int height = (int)(end.Y - start.Y);
            int width = (int)(end.X - start.X);

            // Point in screen space of the center of the region selected
            Vector2 regionCenterScreen = new Vector2((float)(start.X + end.X) / 2f,
                (float)(start.Y + end.Y) / 2f);

            // Generate the projection matrix for the screen region
            Matrix regionProjMatrix = camera.Perspective(0.1f, 100.0f);



            // Calculate the region dimensions in the projection matrix. M11 is inverse of width, M22 is inverse of height.
            regionProjMatrix.M11 /= ((float)width / (float)base.GraphicsDevice.Viewport.Width);
            regionProjMatrix.M22 /= ((float)height / (float)base.GraphicsDevice.Viewport.Height);

            // Calculate the region center in the projection matrix. M31 is horizonatal center.
            regionProjMatrix.M31 = (regionCenterScreen.X - (base.GraphicsDevice.Viewport.Width / 2f))
                / ((float)width / 2f);

            // M32 is vertical center. Notice that the screen has low Y on top, projection has low Y on bottom.
            regionProjMatrix.M32 = -(regionCenterScreen.Y - (base.GraphicsDevice.Viewport.Height / 2f))
                / ((float)height / 2f);


            BoundingFrustum frustum = new BoundingFrustum(camera.ViewMatrix * regionProjMatrix);
            if (entities != null)
            {
                entities.Dragging(frustum);
            }

            if (mouseState.LeftButton == ButtonState.Released)
            {
                state = MouseStates.Release;
            }
        }
        private IActor newUnit = null;

        private void Rest(MouseState mouseState, IEntityController entities, ICameraComponent camera, ITerrainComponent terrain)
        {
            if (GameInformation.CreateUnit)
            {
                if (newUnit == null)
                {
                    newUnit = entities.CreateActor(GameInformation.UnitType);
                }
                Vector2 position = new Vector2();

                position.X = mouseState.X;
                position.Y = mouseState.Y;

                Ray mouseRay = GetRay(position, camera);
                Vector3 goTo = terrain.PickTerrain(mouseRay, camera.Position);
                newUnit.Position = goTo;
            }
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                start = new Vector2(mouseState.X, mouseState.Y);
                state = MouseStates.Click;
            }
        }

        private bool Compare(MouseState mouseState)
        {
            end = new Vector2(mouseState.X, mouseState.Y);
            Vector2 result = start - end;
            if (result.Length() > 2.0f)
            {
                return true;
            }
            return false;
        }


        private Ray GetRay(Vector2 position, ICameraComponent camera)
        {
            Matrix view = camera.ViewMatrix;
            Matrix projection = camera.ProjectionMatrix;

            Matrix worldMatrix = Matrix.Identity;// *Matrix.CreateRotationX(-(float)Math.PI / 2);// *Matrix.CreateRotationY(-(float)Math.PI / 2);


            Vector3 nearSource = new Vector3(position, 5.0f);
            Vector3 farSource = new Vector3(position, 7.0f);

            Vector3 nearPoint = base.Game.GraphicsDevice.Viewport.Unproject(nearSource,
                projection, view, worldMatrix);

            Vector3 farPoint = base.Game.GraphicsDevice.Viewport.Unproject(farSource,
                projection, view, worldMatrix);

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();



            Ray screenPointRay = new Ray(nearPoint, direction);
            return screenPointRay;
        }


        private Vector3 GetCoordinates(Vector2 position, ICameraComponent camera)
        {
            Matrix view = camera.ViewMatrix;
            Matrix projection = camera.ProjectionMatrix;



            Vector3 nearSource = new Vector3(position, 5.0f);
            Vector3 farSource = new Vector3(position, 7.0f);

            Vector3 nearPoint = base.Game.GraphicsDevice.Viewport.Unproject(nearSource,
                projection, view, Matrix.Identity);

            Vector3 farPoint = base.Game.GraphicsDevice.Viewport.Unproject(farSource,
                projection, view, Matrix.Identity);

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();



            Ray screenPointRay = new Ray(nearPoint, direction);

            Plane plane = new Plane(new Vector4(0.0f, 1.0f, 0.0f, 0.0f));
            float? rayIntersect = screenPointRay.Intersects(plane);
            Vector3 ret = new Vector3();
            if (rayIntersect.HasValue)
            {
                ret = screenPointRay.Position + screenPointRay.Direction *

                         rayIntersect.Value;
            }
            return ret;
        }

    }
}